//! 数学计算工具

/// 计算两个数的最大公约数
/// 
/// 使用欧几里得算法计算最大公约数
/// 
/// # 示例
/// 
/// ```
/// use rust_utils::math_utils::gcd;
/// 
/// assert_eq!(gcd(48, 18), 6);
/// assert_eq!(gcd(7, 3), 1);
/// ```
pub fn gcd(a: u64, b: u64) -> u64 {
    if b == 0 {
        a
    } else {
        gcd(b, a % b)
    }
}

/// 计算两个数的最小公倍数
/// 
/// # 示例
/// 
/// ```
/// use rust_utils::math_utils::lcm;
/// 
/// assert_eq!(lcm(4, 6), 12);
/// assert_eq!(lcm(21, 6), 42);
/// ```
pub fn lcm(a: u64, b: u64) -> u64 {
    if a == 0 || b == 0 {
        0
    } else {
        a * b / gcd(a, b)
    }
}

/// 判断一个数是否为质数
/// 
/// # 示例
/// 
/// ```
/// use rust_utils::math_utils::is_prime;
/// 
/// assert_eq!(is_prime(2), true);
/// assert_eq!(is_prime(17), true);
/// assert_eq!(is_prime(15), false);
/// ```
pub fn is_prime(n: u64) -> bool {
    if n <= 1 {
        return false;
    }
    if n <= 3 {
        return true;
    }
    if n % 2 == 0 || n % 3 == 0 {
        return false;
    }
    
    let mut i = 5;
    while i * i <= n {
        if n % i == 0 || n % (i + 2) == 0 {
            return false;
        }
        i += 6;
    }
    true
}

/// 计算阶乘
/// 
/// # 示例
/// 
/// ```
/// use rust_utils::math_utils::factorial;
/// 
/// assert_eq!(factorial(5), 120);
/// assert_eq!(factorial(0), 1);
/// ```
pub fn factorial(n: u64) -> u64 {
    match n {
        0 | 1 => 1,
        _ => n * factorial(n - 1)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_gcd() {
        assert_eq!(gcd(48, 18), 6);
        assert_eq!(gcd(7, 3), 1);
        assert_eq!(gcd(0, 5), 5);
    }

    #[test]
    fn test_lcm() {
        assert_eq!(lcm(4, 6), 12);
        assert_eq!(lcm(21, 6), 42);
        assert_eq!(lcm(0, 5), 0);
    }

    #[test]
    fn test_is_prime() {
        assert_eq!(is_prime(2), true);
        assert_eq!(is_prime(3), true);
        assert_eq!(is_prime(4), false);
        assert_eq!(is_prime(17), true);
        assert_eq!(is_prime(25), false);
        assert_eq!(is_prime(1), false);
        assert_eq!(is_prime(0), false);
    }

    #[test]
    fn test_factorial() {
        assert_eq!(factorial(0), 1);
        assert_eq!(factorial(1), 1);
        assert_eq!(factorial(5), 120);
        assert_eq!(factorial(10), 3628800);
    }
}